package cn.edu.jxau.core.service;

import cn.edu.jxau.core.cache.Cache;
import cn.edu.jxau.core.cache.LFUCache;
import cn.edu.jxau.core.controller.ResultConfig;
import cn.edu.jxau.core.controller.Service;
import cn.edu.jxau.core.controller.ServiceConfig;
import cn.edu.jxau.core.controller.ServiceMapper;
import cn.edu.jxau.core.exception.ServiceException;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


/**
 * 所有业务类的抽取
 * ServiceSupport
 *
 * @author zclong 2017年8月5日
 */
public abstract class ServiceSupport extends Service implements ResultConfig {

    // K: Service类名    V: 所有的业务方法
    private static Cache<String, Method[]> serviceMethodCache = new LFUCache<String, Method[]>(0);
    /**
     * 1、protected访问控制符能被用于方法和成员变量。
     * 2、声明为protected的方法和成员变量能被同一个包里的所有类所访问，就像默认修饰符package一样。
     * 3、能被该类的子类所访问，子类可以和父类不在一个包中
     */
    protected Method[] methods; // 存储缓存里面的所有service方法
    protected String view; // 和service.xml中的name对应
    protected static String result; // service.xml的<result>的name

    protected ServiceSupport() {
    }

    protected ServiceSupport(String view, Map<String, Object[]> params, Map<String, Object> data) {
        /**
         * view service.xml中的name和视图对应
         * params request入口的参数数组
         * data 返回给Servlet的出口参数
         */
        super(view, params, data);
        this.view = view;

        // 根据前台的请求路径从缓存中获取对应的业务方法
        methods = serviceMethodCache.get(this.getClass().getName());
        if (null == methods) {
            //获取子类的所有方法
            methods = this.getClass().getDeclaredMethods();
            //获取子类的所有业务方法
            List<Method> ms = new LinkedList<Method>();
            for (Method method : methods) {
                ms.add(method);
            }
            //覆盖原有
            methods = new Method[ms.size()];
            methods = ms.toArray(methods);
            // 保存缓存
            serviceMethodCache.put(this.getClass().getName(), methods);
        }
    }

    /**
     * 通过获得的对象路经，反射创建Service对象,即配置文件中对应的业务方法
     *
     * @param targetService
     * @param params
     * @param data
     * @return
     * @throws Exception
     * @zclong
     * @2017年8月5日
     * @ReturnType: Service
     */
    @SuppressWarnings("unchecked")
    public static Service createService(ServiceConfig targetService, Map<String, Object[]> params, Map<String, Object> data) throws Exception {
        //传入一个类型的全路径的名字(也就是带包的完整名字)，会返回一个字节码类型(也就是Class类型)的实例
        //clazz = class cn.zclong.SAXReaderXML.service.UserService
        Class<Service> clazz = (Class<Service>) Class.forName(targetService.getClassName());
        Class<?>[] parameterTypes = new Class[]{String.class, Map.class, Map.class};
        //构造函数（Constructor）在对象创建或者实例化时候被调用的方法。通常使用该方法来初始化数据成员和所需资源,通过有参构造函数创建对象
        //constructor = public cn.zclong.SAXReaderXML.service.UserService(java.lang.String,java.util.Map,java.util.Map) throws cn.zclong.SAXReaderXML.service.ServiceException
        Constructor<Service> constructor = clazz.getConstructor(parameterTypes);
        return constructor.newInstance(targetService.getName(), params, data);
    }

    /**
     * 子类执行业务逻辑
     *
     * @param view
     * @param params
     * @param data
     * @throws ServiceException
     * @zclong
     * @2017年8月6日
     * @ReturnType: void
     */
    protected void execute(String view, Map<String, Object[]> params, Map<String, Object> data) throws ServiceException {
        //内部调用方法完成业务
        Method method = getMethod(ServiceMapper.getServiceConfig(view).getMethodName());
        if (null == method) {
            String msg = this.getClass().getSimpleName() + "没有该[" + method.getName() + "]方法";
            throw new ServiceException(msg);
        }
        try {
            method.setAccessible(true); // 若要访问私有方法必须设置setAccessible(true)为true.
            method.invoke(this, params, data); // 完成对应的业务，例如登录login
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取子类的要执行的方法
     *
     * @param methodName
     * @return
     * @zclong
     * @2017年8月6日
     * @ReturnType: Method
     */
    private Method getMethod(String methodName) {
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getName().equals(methodName)) { // 比较方法名，从methods里面获取要执行的方法
                return methods[i];
            }
        }
        return null;
    }

    /**
     * 获取前台传过来数据
     *
     * @param params : request的入口参数数组，Map集合
     * @param key    : Map数组的key，通过它获取对应的值，即用户名
     * @param index  : 因为是Map集合，即一个key对应的value是一个数组，所以给定数值的下标，获取对应得值，如果数组中只有一，则下标位0
     * @return
     * @zclong
     * @2017年8月6日
     * @ReturnType: String
     */
    protected String getParamStringValue(Map<String, Object[]> params, String key, int index) {
        return (String) getParamsValue(params, key, index);
    }

    /**
     * T类型为Object[]
     * 获取Map<Srting, Object[]>中指定key和Object[]中index位置的数据
     *
     * @param params
     * @param key    : Map的key
     * @param index  : Object[]中的位置
     * @return
     * @zclong
     * @2017年8月6日
     * @ReturnType: Object
     */
    private <T> Object getParamsValue(Map<String, T> params, String key, int index) {
        T value = params.get(key);
        if (null == value) {
            return null;
        }
        Object[] values = (Object[]) value;
        if (index > values.length) {
            return null;
        }
        return values[index];
    }
}
